﻿#include "Thread.h"
#include "CurrentThread.h"

#include <type_traits>
#include <cstdlib>
#include <cstdio>
#include <errno.h>
#include <time.h>
#include <assert.h>

#if defined(_MSC_VER)
#include <Winsock2.h>
#include <Windows.h>
#pragma comment(lib,"ws2_32.lib")
#elif defined(__GNUC__)
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/unistd.h>
#endif


#if (defined(_MSC_VER))
#include <windows.h>
const DWORD MS_VC_EXCEPTION=0x406D1388;

#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
   DWORD dwType; // Must be 0x1000.
   LPCSTR szName; // Pointer to name (in user addr space).
   DWORD dwThreadID; // Thread ID (-1=caller thread).
   DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)

void SetThreadName( DWORD dwThreadID, const char* threadName)
{
   THREADNAME_INFO info;
   info.dwType = 0x1000;
   info.szName = threadName;
   info.dwThreadID = dwThreadID;
   info.dwFlags = 0;

   __try
   {
      RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
   }
   __except(EXCEPTION_EXECUTE_HANDLER)
   {
   }
}
#endif

namespace _zhy_muduo
{
	using namespace std;

namespace CurrentThread
{
#if (defined(_MSC_VER))
	__declspec(thread) CurrentThread::tid_t t_cachedTid;
	__declspec(thread) char t_tidString[32];
	__declspec(thread) int t_tidStringLength = 6;
	__declspec(thread) const char* t_threadName = "unknown";
	const bool sameType = std::is_same<DWORD, tid_t>::value;
	static_assert(sameType, "is_same<DWORD, tid_t> false");
#elif (defined(__GNUC__))
	__thread tid_t t_cachedTid = 0;
	__thread char t_tidString[32];
	__thread int t_tidStringLength = 6;
	__thread const char *t_threadName = "unknown";
	const bool sameType = std::is_same<int, tid_t>::value;
	static_assert(sameType, "is_same<int, tid_t> false");
#else
	static_assert(false, "No instantiation, CHECK SYS!");
#endif

}//end namespace CurrentThread

namespace detail{
#if (defined(_MSC_VER))
CurrentThread::tid_t gettid()
{
	return GetCurrentThreadId();
}
#elif (defined(__GNUC__))
CurrentThread::tid_t gettid()
{
	return static_cast<pid_t>(::syscall(SYS_gettid));
}
#else
	static_assert(false, "No instantiation, CHECK SYS!");
#endif

void afterFork()
{
	_zhy_muduo::CurrentThread::t_cachedTid = 0;
	_zhy_muduo::CurrentThread::t_threadName = "main";
	CurrentThread::tid();
	// no need to call pthread_atfork(NULL, NULL, &afterFork);
}

class ThreadNameInitializer
{
 public:
ThreadNameInitializer()
{
	_zhy_muduo::CurrentThread::t_threadName = "main";
	CurrentThread::tid();
#if (defined(_MSC_VER))
	//FIXME:Windows fork
#elif (defined(__GNUC__))
	pthread_atfork(NULL, NULL, &afterFork);
#else
		static_assert(false, "No instantiation, CHECK SYS!");
#endif
}
};

ThreadNameInitializer init;

struct ThreadData
{
	typedef _zhy_muduo::Thread::ThreadFunc ThreadFunc;
	ThreadFunc func_;
	string name_;
	weak_ptr<CurrentThread::tid_t> wkTid_;

	ThreadData(const ThreadFunc& func,
				const string &name,
				const shared_ptr<CurrentThread::tid_t> &tid)
		: func_(func),
		name_(name),
		wkTid_(tid)
	{ }

	void runInThread()
	{
		CurrentThread::tid_t tid = CurrentThread::tid();
		shared_ptr<CurrentThread::tid_t> ptid = wkTid_.lock();
		if (ptid) {
			*ptid = tid;
			ptid.reset();
		}

		CurrentThread::t_threadName = name_.empty() ? "_zhy_muduoThread" : name_.c_str();

#if (defined(_MSC_VER))
		SetThreadName(tid, _zhy_muduo::CurrentThread::t_threadName);
#elif (defined(__GNUC__))
		::prctl(PR_SET_NAME, _zhy_muduo::CurrentThread::t_threadName);
#else
		static_assert(false, "No instantiation, CHECK SYS!");
#endif

		try {
			func_();
			CurrentThread::t_threadName = "finished";
		}
#if 0
		//FIXME 完成Exception类后放开 
		catch (const Exception &ex) {
			CurrentThread::t_threadName = "crashed";
			fprintf(stderr, "exception caught in Thread %s\n", name_.c_str());
			fprintf(stderr, "reason: %s\n", ex.what());
			abort();
		}
#endif
		catch (const std::exception &ex) {
			CurrentThread::t_threadName = "crashed";
			fprintf(stderr, "exception caught in Thread %s\n", name_.c_str());
			fprintf(stderr, "reason: %s\n", ex.what());
			abort();
		}
		catch (...) {

			CurrentThread::t_threadName = "crashed";
			fprintf(stderr, "unknown exception caught in Thread %s\n", name_.c_str());
			throw; //rethrow
		}
	}

};//end struct ThreadData

void* startThread(void *obj)
{
	ThreadData *data = static_cast<ThreadData*>(obj);
	data->runInThread();
	delete data;
	return NULL;
}

}//end namespace detail

}//end namespace _zhy_muduo


using namespace _zhy_muduo;

void CurrentThread::cacheTid()
{
	if (t_cachedTid == 0)
	{
		t_cachedTid = _zhy_muduo::detail::gettid();
#if (defined(_MSC_VER))
		t_tidStringLength = _snprintf(t_tidString, sizeof(t_tidString), "%5d ", t_cachedTid);
#elif (defined(__GNUC__))
		t_tidStringLength = snprintf(t_tidString, sizeof(t_tidString), "%5d ", t_cachedTid);
#else
		static_assert(false, "No instantiation, CHECK SYS!");
#endif
	}
}

#if (defined(_MSC_VER))
#include <tlhelp32.h>
DWORD GetMainThreadId(DWORD processId = 0)
{
	if(processId == 0)
		processId = GetCurrentProcessId();

	DWORD threadId = 0;
	THREADENTRY32 te32 = { sizeof(te32) };
	HANDLE threadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
	if (Thread32First(threadSnap, &te32)) {
		do {
			if( processId == te32.th32OwnerProcessID ) {
				threadId = te32.th32ThreadID;
				break;
			}
		} while (Thread32Next(threadSnap, &te32));
	}
	return threadId;
}
#endif

bool CurrentThread::isMainThread()
{
#if (defined(_MSC_VER))
	return tid() ==  GetMainThreadId(GetCurrentProcessId());
#elif (defined(__GNUC__))
	return tid() == ::getpid();
#else
	static_assert(false, "No instantiation, CHECK SYS!");
#endif
}


void CurrentThread::sleepUsec(int64_t usec)
{
	const int kMicroSecondsPerSecond = 1000 * 1000;
#if (defined(_MSC_VER))
	LARGE_INTEGER m_liPerfFreq = { 0 };
	if (!QueryPerformanceFrequency(&m_liPerfFreq)) {
		printf("你的当前计算机硬件不支持高精度计时器\n");
		return;
	}

	LARGE_INTEGER m_liPerfStart = { 0 };
	QueryPerformanceCounter(&m_liPerfStart);

	LARGE_INTEGER liPerfNow = { 0 };
	for (;;) {
		QueryPerformanceCounter(&liPerfNow);
		double time = (((liPerfNow.QuadPart -
			m_liPerfStart.QuadPart) * 1000000) / (double)m_liPerfFreq.QuadPart);
		if (time >= usec)
			break;
	}
#elif (defined(__GNUC__))
	struct timespec ts = { 0, 0 };
	ts.tv_sec = static_cast<time_t>(usec / kMicroSecondsPerSecond);
	ts.tv_nsec = static_cast<long>(usec % kMicroSecondsPerSecond * 1000);
	::nanosleep(&ts, NULL);
#else
	static_assert(false, "No instantiation, CHECK SYS!");
#endif
}


atomic<int32_t> Thread::numCreated_;

#if (defined(_MSC_VER))
//FIXME: to check pthreadId_
Thread::Thread(const ThreadFunc& func, const std::string& name)
	:started_(false),
	joined_(false),
	pthreadId_(),
	tid_(new CurrentThread::tid_t(0)),
	func_(func),
	name_(name)
{
	setDefaultName();
}
#elif (defined(__GNUC__))
Thread::Thread(const ThreadFunc& func, const std::string& name)
	:started_(false),
	joined_(false),
	pthreadId_(0),
	tid_(new CurrentThread::tid_t(0)),
	func_(func),
	name_(name)
{
	setDefaultName();
}
#else
	static_assert(false, "No instantiation, CHECK SYS!");
#endif


#if (defined(_MSC_VER))
//FIXME: to check pthreadId_
Thread::Thread(ThreadFunc&& func, const std::string& name)
	:started_(false),
	joined_(false),
	pthreadId_(),
	tid_(new CurrentThread::tid_t(0)),
	func_(move(func)),
	name_(name)
{
	setDefaultName();
}
#elif (defined(__GNUC__))
Thread::Thread(ThreadFunc&& func, const std::string& name)
	:started_(false),
	joined_(false),
	pthreadId_(0),
	tid_(new CurrentThread::tid_t(0)),
	func_(move(func)),
	name_(name)
{
	setDefaultName();
}
#else
	static_assert(false, "No instantiation, CHECK SYS!");
#endif

Thread::~Thread()
{
	if (started_ && !joined_) {
		pthread_detach(pthreadId_);
	}
}
void Thread::setDefaultName()
{
	int num = numCreated_++;
	if (name_.empty()) {
		char buf[32];
#if (defined(_MSC_VER))
		_snprintf_s(buf, sizeof buf, "Thread%d", num);
#elif (defined(__GNUC__))
		snprintf(buf, sizeof buf, "Thread%d", num);
#else
	static_assert(false, "No instantiation, CHECK SYS!");
#endif
		name_ = buf;
	}
}

void Thread::start()
{
	assert(!started_);
	started_ = true;
	detail::ThreadData *data = new detail::ThreadData(func_, name_, tid_);
	if (pthread_create(&pthreadId_, NULL, &detail::startThread, data)) {
		started_ = false;
		delete data;
		fprintf(stderr, "Failed in pthread_create");
	}
}

int Thread::join()
{
	assert(started_);
	assert(!joined_);
	joined_ = true;
	return pthread_join(pthreadId_, NULL);
}